using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Input.Touch;
using Microsoft.Xna.Framework.Media;


// http://box2dxna.codeplex.com
using Box2D.XNA;

namespace WP7Box2d
{
    class GameEngine : ButtonListenerIF
    {

        private Body m_activeBody = null;

        private Vector2 m_line1;
        private Vector2 m_line2;

        private Matrix transform = Matrix.Identity;
        private float[] camPos = { 0, 0 };

        private bool m_ammonFired = false;
        private int m_ammonResetCounter;

        public enum GameState
        {
            ESplashScreenState = 0,
            EMenuScreenState = 1,
            EGameScreensState = 2,
            EGameEditorState = 3,
            EDemoScreensState = 4,
            ELevelPreviewState = 5
        }
        public GameState m_gameState = GameState.ESplashScreenState;

        private double m_gameTime;

        Texture2D m_lineTexture;

        Menu m_menu = null;

        Button m_cancelBtn = null;

        float m_cameraOffset = 0.0f;
        float m_cameraStartXPos = 0.0f;
        Body m_ammonBody = null;

        // Box2D
        private BoxHandler m_boxHandler = null;
        
        private LevelHandler m_levelHandler = null;

        private LevelPreview m_levelPreview = null;

        private Audio m_audio = null;

        private Game m_game;

        public GameEngine(Game game, ref BoxHandler box, ref LevelHandler level, ref LevelPreview levelP)
        {
            transform = Matrix.CreateScale(1.00f);

            m_game = game;
            m_cameraStartXPos = 0.0f;
            m_cameraOffset = 0.0f;
            m_boxHandler = box;
            m_levelHandler = level;
            m_levelPreview = levelP;
            m_line1.X = 0;
            m_line1.Y = 0;
            m_line2.X = 0;
            m_line2.Y = 0;

            // Listen events
            m_levelHandler.GameStateChanged += new LevelHandler.GameStateChangedEventHandler(GameStateChanged);
            m_levelPreview.GameStateChangedFromPreview += new LevelHandler.GameStateChangedEventHandler(GameStateChanged);
            m_boxHandler.GameOverHitEvent += new BoxHandler.GameOverEvent(GameOverHitEvent); 

            m_audio = new Audio();
            m_boxHandler.SetAudio(m_audio);

            m_menu = new Menu(this);
        }

        void ResetEngine()
        {
            m_cameraStartXPos = 0.0f;
            m_cameraOffset = 0.0f;

            m_line1.X = 0;
            m_line1.Y = 0;
            m_line2.X = 0;
            m_line2.Y = 0;
        }

        void ResetBoxWorld()
        {
            m_ammonBody = null;
            m_boxHandler.ClearBoxWorld();
            m_ammonBody = m_boxHandler.CreateAmmon();
        }

        void ResetAmmon()
        {
            m_ammonBody = null;
            m_boxHandler.DeleteAmmon();
            m_ammonBody = m_boxHandler.CreateAmmon();
        }

        void GameOverHitEvent()
        {
            ButtonPressed(Button.ButtonType.ECancelBtn);
        }

        public void GameStateChanged(GameEngine.GameState newState)
        {
            System.Diagnostics.Debug.WriteLine("GameStateChanged " +newState);

            switch (newState)
            {
                case GameState.EMenuScreenState:
                    {
                        if (m_gameState != GameState.ESplashScreenState)
                            m_audio.Stop();

                        ResetEngine();
                        ResetBoxWorld();
                        m_levelHandler.Clear();
                        m_levelPreview.Clear();
                        m_gameState = GameState.EMenuScreenState;
                        break;
                    }
                case GameState.EGameScreensState:
                    {
                        ResetEngine();
                        ResetBoxWorld();
                        m_boxHandler.LoadContent();
                        m_levelHandler.LoadContent();
                        m_gameState = GameState.EGameScreensState;
                        break;
                    }
                case GameState.EDemoScreensState:
                    {
                        // Do not reset box2w, we are demoing it
                        ResetEngine();
                        //ResetBoxWorld();
                        m_boxHandler.LoadContent();
                        m_levelHandler.LoadContent();
                        m_gameState = GameState.EGameScreensState;
                        break;
                    }
            }
        }

        public void LoadContent(ContentManager cm)
        {
            // Dot for line
            m_lineTexture = cm.Load<Texture2D>("dotForLine");

            // Create ammon
            m_ammonBody = m_boxHandler.CreateAmmon();
            
            // Create menu
            m_menu.SetPosition(new Rectangle(20, 20, (int)m_boxHandler.m_size.X-20, (int)m_boxHandler.m_size.Y-20));
            m_menu.LoadContent(cm);

            // Cancel button
            Texture2D texture = cm.Load<Texture2D>("cancel_btn");
            m_cancelBtn = new Button(this, Button.ButtonType.ECancelBtn, texture);
            m_cancelBtn.SetPosition(new Rectangle((int)m_boxHandler.m_size.X - texture.Width - 10, 10, texture.Width, texture.Height));

            m_audio.LoadContent(cm);
            m_audio.PlayGeneralAudioLoop(true);
        }

        public void ButtonPressed(Button.ButtonType buttonType)
        {
            System.Diagnostics.Debug.WriteLine("GameEngine " + buttonType);
            switch (buttonType)
            {
                case Button.ButtonType.ECreateBtn:
                    {
                        m_audio.Stop();
                        m_audio.PlayEditorAudioLoop(true);

                        m_boxHandler.LoadContent();
                        m_levelHandler.LoadContent();

                        m_levelPreview.UpdateLevelIndex();

                        ResetEngine();
                        ResetBoxWorld();
                        m_levelHandler.CreateLevel();
                        m_gameState = GameState.EGameEditorState;
                        break;
                    }
                case Button.ButtonType.EPlayBtn:
                    {
                        if (m_levelPreview.m_levelStorage.LevelExists() == false)
                        {
                            System.Windows.MessageBox.Show("No any levels found. CREATE level first");
                        }
                        else
                        {
                            m_audio.Stop();
                            m_audio.PlayGameAudioLoop(true);

                            m_boxHandler.LoadContent();
                            m_levelHandler.LoadContent();

                            ResetEngine();
                            ResetBoxWorld();
                            m_levelPreview.LoadContent();
                            m_levelPreview.LoadAndShowLevels();
                            m_gameState = GameState.ELevelPreviewState;
                        }

                        break;
                    }
                case Button.ButtonType.ECancelBtn:
                    {
                        m_audio.Stop();

                        ResetEngine();
                        ResetBoxWorld();
                        m_gameState = GameState.EMenuScreenState;
                        break;
                    }
                case Button.ButtonType.EExitBtn:
                    {
                        m_audio.Stop();
                        m_game.Exit();
                        break;
                    }
            }
        }

        public void Update(GameTime gameTime)
        {
            m_audio.UpdateAudio();

            // State handling
            if (m_gameState == GameState.ESplashScreenState)
            {
                m_gameTime += gameTime.ElapsedGameTime.TotalSeconds;
                if (m_gameTime > 3.0)
                {
                    m_gameState = GameState.EMenuScreenState;
                }
            }
            else if (m_gameState == GameState.EGameScreensState)
            {
                Vector2 touchPosition;
                TouchCollection touchCollection = TouchPanel.GetState();
                if (touchCollection.Count() > 0)
                {
                    TouchLocation tl = touchCollection.First();

                    touchPosition = tl.Position;

                    m_cancelBtn.handleTouch(touchPosition);

                    if (tl.State == TouchLocationState.Pressed)
                    {
                        // Press the ammon for firing
                        m_line1 = touchPosition;
                        m_line2.X = 0;
                        m_line2.Y = 0;

                        Body touchedBody = null;
                        touchPosition.X = touchPosition.X + m_cameraOffset;
                        if (m_boxHandler.isAmmonBodyTouched(ref touchedBody, touchPosition))
                        {
                            m_activeBody = touchedBody;
                            if (touchedBody != null)
                            {
                                Vector2 fixedCenter = touchedBody.GetPosition();
                                fixedCenter.X = fixedCenter.X * m_boxHandler.pixelsFromMeters;
                                fixedCenter.Y = fixedCenter.Y * m_boxHandler.pixelsFromMeters;
                                fixedCenter.X -= m_cameraOffset;
                                m_line1 = fixedCenter;
                            }
                        }
                    }
                    else if (tl.State == TouchLocationState.Moved)
                    {
                        if (m_activeBody != null)
                        {
                            m_line2 = touchPosition;
                        }

                    }
                    else if (tl.State == TouchLocationState.Released)
                    {
                        // Release the ammon for firing it
                        float x = m_line1.X - touchPosition.X;
                        float y = m_line1.Y - touchPosition.Y;
                        m_line2.X = 0;
                        m_line2.Y = 0;
                        if (m_activeBody != null)
                        {
                            m_activeBody.SetBullet(true);
                            // force x, y
                            m_activeBody.ApplyLinearImpulse(new Vector2(x * 0.5f, y * 0.5f), m_activeBody.GetWorldCenter());
                            m_activeBody = null;
                            m_ammonFired = false;
                            m_audio.PlayAmmon();
                        }
                    }
                }

                // Is ammon moving?
                if (m_ammonBody != null && m_ammonBody.IsAwake())
                {
                    //System.Diagnostics.Debug.WriteLine("Ammon moving");
                    float av = Math.Abs(m_ammonBody.GetAngularVelocity());
                    if (av > 1.5f && !m_ammonFired)
                    {
//                        System.Diagnostics.Debug.WriteLine("Ammon fired");
                        m_ammonFired = true;
                        m_ammonResetCounter = 30;
                    }
                    if (av < 1.5f && m_ammonFired)
                    {
                        m_ammonResetCounter--;
                        if (m_ammonResetCounter < 0)
                        {
//                            System.Diagnostics.Debug.WriteLine("RESET Ammon");
                            ResetAmmon();
                            m_ammonFired = false;
                        }
                    }
                    //System.Diagnostics.Debug.WriteLine(m_ammonBody.GetAngularVelocity());
                }

                // Calculate ammon position and camera offset for moving camera
                // to ammon position
                Vector2 ammonPos = m_ammonBody.GetPosition();
                float ammonPosF = ammonPos.X * m_boxHandler.pixelsFromMeters;
                if (ammonPosF > 400 && m_cameraStartXPos == 0)
                {
                    // Start value for offset calculation
                    m_cameraStartXPos = ammonPosF;

                }
                else if (ammonPosF <= 400)
                {
                    m_cameraStartXPos = 0.0f;
                    m_cameraOffset = 0.0f;
                }


                // Updata Box2D
                m_boxHandler.Update(gameTime);

                // Calculate offset
                if (m_cameraStartXPos > 0)
                {
                    float newAmmonPosF = m_ammonBody.GetPosition().X * m_boxHandler.pixelsFromMeters;
                    m_cameraOffset = newAmmonPosF - m_cameraStartXPos;
                }
            }
            else if (m_gameState == GameState.EGameEditorState)
            {
                m_levelHandler.Update(gameTime, m_cameraOffset);
            }
            else if (m_gameState == GameState.ELevelPreviewState)
            {
                m_levelPreview.Update(gameTime, m_cameraOffset);
            }
            else if (m_gameState == GameState.EMenuScreenState)
            {
                m_menu.Update(gameTime);
            }
        }


        // Custon draw line method
        private void DrawLine(SpriteBatch spriteBatch, Vector2 a, Vector2 b)
        {
            if (b.X == 0 || b.Y == 0)
                return;

            Vector2 Origin = new Vector2(0.5f, 0.0f);
            Vector2 diff = b - a;
            float angle;
            Vector2 Scale = new Vector2(1.0f, diff.Length() / m_lineTexture.Height);

            angle = (float)(Math.Atan2(diff.Y, diff.X)) - MathHelper.PiOver2;

            spriteBatch.Draw(m_lineTexture, a, null, Color.White, angle, Origin, Scale, SpriteEffects.None, 1.0f);
        }

        public void Draw(SpriteBatch spriteBatch, GameTime gameTime)
        {
            switch (m_gameState)
            {
                case GameState.ESplashScreenState:
                    {
                        // Draw splash screen only
                        m_boxHandler.DrawSplashScreen(spriteBatch, m_cameraOffset);
                        break;
                    }
                case GameState.ELevelPreviewState:
                    {
                        // Sky, ground...
                        m_boxHandler.DrawSkyAndGround(spriteBatch, m_cameraOffset);

                        m_levelPreview.Draw(spriteBatch, gameTime);
                        break;
                    }
                case GameState.EGameScreensState:
                    {
                        // Draw game only
                        // Sky, ground...
                        m_boxHandler.DrawWorld(spriteBatch, m_cameraOffset);
                        // Box2D
                        m_boxHandler.DrawBoxList(spriteBatch, m_cameraOffset);
                        m_boxHandler.DrawEnemyBoxList(spriteBatch, m_cameraOffset);
                        // Draw line for showing item shooting angle
                        DrawLine(spriteBatch, m_line1, m_line2);
                        // Cancel btn
                        m_cancelBtn.Draw(spriteBatch, gameTime);

                        break;
                    }
                case GameState.EGameEditorState:
                    {
                        // Sky, ground...
                        m_boxHandler.DrawSkyAndGround(spriteBatch, m_cameraOffset);
                        // Draw level handler
                        m_levelHandler.Draw(spriteBatch, gameTime);
                        break;
                    }
                case GameState.EMenuScreenState:
                    {
                        m_boxHandler.DrawSky(spriteBatch);
                        m_menu.Draw(spriteBatch, gameTime);                        
                        break;
                    }
                default:
                    {
                        break;
                    }
            }
        }



    }
}
